﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Browser;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace CentralFoveaLab
{
    public partial class MainPage : UserControl, IVectorVisualizerUI
    {
        public MainPage()
        {
            InitializeComponent();

            this.Visualizer = new VectorVisualizer(this);

            this.Visualizer.Lines.ReDrawn += this.Lines_ReDrawn;

            this.Visualizer.Initialize();

            this.MouseLeftButtonDown += this.Page_MouseLeftButtonDown;
            this.MouseLeftButtonUp += this.Page_MouseLeftButtonUp;
            this.MouseLeave += this.Page_MouseLeave;
            this.MouseMove += this.Page_MouseMove;

            this.MouseWheel += this.Page_MouseWheel;
            this.Loaded += Page_Loaded;
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            this.LoadImagePixFile();
        }
        private void LoadImagePixFile()
        {
            var configFile = "20131212181.txt";
            var url = new Uri(configFile + "?r=" + DateTime.Now.Ticks, UriKind.Relative);
            var client = new WebClient();
            client.DownloadStringCompleted += this.client_DownloadStringCompleted;
            client.DownloadStringAsync(url);

        }


        private void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                HtmlPage.Window.Alert("Cannot load configuration file expected to be in the 'ClientBin' folder");
                return;
            }

            var pointFile = e.Result;

            String[] lines = pointFile.Split(';');
            if (lines == null || lines.Length <= 0) return;
            var list = new List<CPoint>(128 * 128);
            // int sub = 10;
            foreach (var item in lines)
            {
                String[] line = item.Split(',');
                if (line.Length == 3)
                {
                    byte c = Convert.ToByte(line[2]);

                    CPoint p = new CPoint()
                    {
                        X = Convert.ToInt32(line[0]),
                        Y = Convert.ToInt32(line[1]),
                        Z = Convert.ToInt32(line[2]),
                        Color = Color.FromArgb(100, c, c, c)
                    };
                    list.Add(p);
                }
                else if (line.Length == 6)
                {
                    //     if (Convert.ToInt32(line[3]) < sub && Convert.ToInt32(line[4]) < sub && Convert.ToInt32(line[5]) < sub) continue;
                    byte r = Convert.ToByte(line[3]);
                    byte g = Convert.ToByte(line[4]);
                    byte b = Convert.ToByte(line[5]);
                    // if (b == 255 )
                    {
                        CPoint p = new CPoint()
                        {
                            X = Convert.ToInt32(line[0]),
                            Y = Convert.ToInt32(line[1]),
                            Z = (r+g+b)/3,
                            Color = Color.FromArgb(255, r, g, b)
                        };
                        Console.WriteLine(p.Z);
                        list.Add(p);
                    }
                }
            }
            int rate = 20;
            int k = 10;
            for (int i = 0; i < list.Count; i++)
            {
                var item = list[i];
                // if(i%2==0)
                ValidateAndAddVector(
                    item.X * rate / 128 - k,
                    -item.Y * rate / 128 + k,
                    item.Z *5 / 256 - 0,
                    item.X * rate / 128 - k,
                    -item.Y * rate / 128 + k,
                    item.Z *5 / 256 - 0,
                    item.Color);
            }
            this.Visualizer.EndAddVector();
        }
        private void ValidateAndAddVector(double x1, double y1, double z1, double x2, double y2, double z2, Color color)
        {
            var vector = new VectorUI(_VectorFactor)
            {
                BeginningX = x1,
                BeginningY = y1,
                BeginningZ = z1,
                EndX = x2,
                EndY = y2,
                EndZ = z2,
            };
            if (vector.HasValuesExceeding(_VectorLimit))
            {
                HtmlPage.Window.Alert(String.Format("Vector {0} cannot have values bigger that {1} or lower than {2}",vector, _VectorLimit, _VectorLimit * -1));
                return;
            }
            if (this.Visualizer.Vectors.Find((vUI, i) => vUI.Equals(vector)) != null)
            {
                HtmlPage.Window.Alert(String.Format("Vector {0} is already defined",vector));
                return;
            }
            vector.Brush = new SolidColorBrush(color);

            this.Visualizer.BeginAddVector(vector);
        }

        private void btnRestPos_Click(object sender, RoutedEventArgs e)
        {
            this.Visualizer.ClearVectors();
            LoadImagePixFile();
        }

        private void Page_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            var sign = e.Delta > 0 ? 1 : -1;
            e.Handled = true;
            this.Visualizer.Lines.DefaultZoom += 0.2 * sign;

            this.Visualizer.Lines.ReDraw();
        }

        private void Lines_ReDrawn(object sender, EventArgs e)
        {
            if (!this._isOverSliderX) this.SliderX.Value = ((this.Visualizer.Lines.RotationDegreesX + 180) / 360D) * 10;
            if (!this._isOverSliderY) this.SliderY.Value = ((this.Visualizer.Lines.RotationDegreesY + 180) / 360D) * 10;
            if (!this._isOverSliderZ) this.SliderZ.Value = ((this.Visualizer.Lines.RotationDegreesZ + 180) / 360D) * 10;

            this.DegreesX.Text = this.Visualizer.Lines.RotationDegreesX.ToString();
            this.DegreesY.Text = this.Visualizer.Lines.RotationDegreesY.ToString();
            this.DegreesZ.Text = this.Visualizer.Lines.RotationDegreesZ.ToString();
        }

        private void Page_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this._isDragging = true;
            this._mouseLastPoint = e.GetPosition(this.GraphContainer);
        }

        private void Page_MouseMove(object sender, MouseEventArgs e)
        {
            if (!this._isDragging)
            {
                return;
            }
            var mousePoint = e.GetPosition(this.GraphContainer);
            if (e.GetPosition(this.LayoutRoot).X > 600)
            {
                this._isDragging = false;
                return;
            }
            else
            {
                this._isOverSliderX = this._isOverSliderY = this._isOverSliderZ = false;
            }

            this.Visualizer.MouseMove(
                (mousePoint.X - this._mouseLastPoint.X) * COEF_MOVE,
                (mousePoint.Y - this._mouseLastPoint.Y) * COEF_MOVE
            );
            this._mouseLastPoint = mousePoint;
        }
        private void UndoMouseDown()
        {
            this._isDragging = false;
        }

        private void ResetBtn_Click(object sender, RoutedEventArgs e)
        {
            this.Visualizer.SetInitialPosition();
        }

        private void Page_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            this.UndoMouseDown();
        }
        private void Page_MouseLeave(object sender, MouseEventArgs e)
        {
            this.UndoMouseDown();
            this._isOverSliderX = this._isOverSliderY = this._isOverSliderZ = false;
        }



        private void DegreesX_LostFocus(object sender, RoutedEventArgs e)
        {
            this.VerifyAndSetAxisValue(
                    sender as TextBox,
                    (degrees, visualizer) =>
                    {
                        visualizer.Lines.RotateToDegreesAroundX(degrees);
                        this.Visualizer.Lines.ReDraw();
                        return degrees.ToString();
                    });
        }

        private void DegreesY_LostFocus(object sender, RoutedEventArgs e)
        {
            this.VerifyAndSetAxisValue(
                    sender as TextBox,
                    (degrees, visualizer) =>
                    {
                        visualizer.Lines.RotateToDegreesAroundY(degrees);
                        this.Visualizer.Lines.ReDraw();
                        return degrees.ToString();
                    });
        }

        private void DegreesZ_LostFocus(object sender, RoutedEventArgs e)
        {
            this.VerifyAndSetAxisValue(
                    sender as TextBox,
                    (degrees, visualizer) =>
                    {
                        visualizer.Lines.RotateToDegreesAroundZ(degrees);
                        this.Visualizer.Lines.ReDraw();
                        return degrees.ToString();
                    });
        }

        private void DegreesX_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.DegreesX_LostFocus(sender, e);
            }
        }

        private void DegreesY_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.DegreesY_LostFocus(sender, e);
            }
        }

        private void DegreesZ_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.DegreesZ_LostFocus(sender, e);
            }
        }


        private void Vector_TextChanged(object sender, EventArgs e)
        {
            this.VerifyVectorValue(sender as TextBox);
        }

        private void Vector_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                this.Vector_TextChanged(sender, e);
            }
        }
        private void VerifyAndSetAxisValue(TextBox textBox, Func<double, VectorVisualizer, string> func)
        {
            var text = textBox.Text.Trim().Replace(',', '.');
            if (text == String.Empty)
            {
                textBox.Text = func(0, this.Visualizer);
                textBox.Select(textBox.Text.Length, 0);
                return;
            }
            double degrees;
            if (Double.TryParse(text, out degrees))
            {
                degrees = degrees % 360;
                textBox.Text = func(degrees, this.Visualizer);
                textBox.Select(textBox.Text.Length, 0);
                return;
            }
            HtmlPage.Window.Alert("Please Enter Numeric Value");
            textBox.Text = func(0, this.Visualizer);
            textBox.Select(textBox.Text.Length, 0);
        }

        private void VerifyVectorValue(TextBox textBox)
        {
            var text = textBox.Text.Trim().Replace(',', '.');
            if (text == "-")
            {
                return;
            }
            if (text == String.Empty)
            {
                textBox.Text = 0.ToString();
                return;
            }
            double value;
            if (Double.TryParse(text, out value))
            {
                if (value < _VectorLimit * -1 || value > _VectorLimit)
                {
                    HtmlPage.Window.Alert(String.Format("The value must be between -{0} and {0}!",_VectorLimit));
                    textBox.Text = (value > 0 ? _VectorLimit : _VectorLimit * -1).ToString();
                }
                return;
            }
            HtmlPage.Window.Alert("Please Enter Numeric Value");
            textBox.Text = 0.ToString();
        }
        #region Slider


        private void SliderX_MouseEnter(object sender, EventArgs e)
        {
            this._isOverSliderX = true;
        }

        private void SliderY_MouseEnter(object sender, EventArgs e)
        {
            this._isOverSliderY = true;
        }

        private void SliderZ_MouseEnter(object sender, EventArgs e)
        {
            this._isOverSliderZ = true;
        }

        private void SliderX_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            var slider = (Slider)sender;
            if (!this._isOverSliderX || !slider.IsFocused)
            {
                return;
            }
            this.Visualizer.Lines.ShiftDegreesAroundX(((e.NewValue / 10) * 360 - 180) - this.Visualizer.Lines.RotationDegreesX);
            this.Visualizer.Lines.ReDraw();
        }
        private void SliderY_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            var slider = (Slider)sender;
            if (!this._isOverSliderY || !slider.IsFocused)
            {
                return;
            }
            this.Visualizer.Lines.ShiftDegreesAroundY(((e.NewValue / 10) * 360 - 180) - this.Visualizer.Lines.RotationDegreesY);
            this.Visualizer.Lines.ReDraw();
        }
        private void SliderZ_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            var slider = (Slider)sender;
            if (!this._isOverSliderZ || !slider.IsFocused)
            {
                return;
            }
            this.Visualizer.Lines.ShiftDegreesAroundZ(((e.NewValue / 10) * 360 - 180) - this.Visualizer.Lines.RotationDegreesZ);
            this.Visualizer.Lines.ReDraw();
        }

        #endregion


        #region
        public VectorVisualizer Visualizer { get; private set; }
        /// <summary>
        /// Used to slow down the movement
        /// </summary>
        private const double COEF_MOVE = 0.3;
        private const string VECTORS_CHANGE_JS_FUNC = "VV_ChangeVectors";

        private static readonly Brush _AxisDefaultBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0x77, 0x77, 0x77));
        private static readonly Brush _SubAxisDefaultBrush = new SolidColorBrush(Color.FromArgb(0xFF, 0xE1, 0xE1, 0xE1));
        private const double _AxisDefaultLength = 216.0;
        private static readonly Point _AxisDefaultCenter = new Point(290, 280);
        private const double _AxisArrowSize = 10.0;
        private const double _LetterShift = 5.0;
        private const double _LetterWidth = 10.0;
        private const double _LetterHeight = 20.0;
        private const double _VectorFactor = 20.0;
        private const double _VectorLimit = 10.0;




        private bool _isDragging;
        private bool _isOverSliderX;
        private bool _isOverSliderY;
        private bool _isOverSliderZ;
        private Point _mouseLastPoint;
        private string _currentPageUrl;



        private static readonly Color[] Colors =
            new[]
            {
                Color.FromArgb(0xFF,0x00,0x00,0x99),
                Color.FromArgb(0xFF,0x99,0x00,0x00),
                Color.FromArgb(0xFF,0x00,0x66,0x00),
                Color.FromArgb(0xFF,0x66,0x33,0x00),
                Color.FromArgb(0xFF,0x00,0x33,0x66),
                Color.FromArgb(0xFF,0x66,0x66,0x00),
                Color.FromArgb(0xFF,0x00,0x66,0x66),
                Color.FromArgb(0xFF,0x33,0x66,0x00),
                Color.FromArgb(0xFF,0x00,0x66,0x33),
                Color.FromArgb(0xFF,0x66,0x00,0x66),
                Color.FromArgb(0xFF,0x33,0x66,0x33),
                Color.FromArgb(0xFF,0x33,0x00,0x33),
                Color.FromArgb(0xFF,0x66,0x33,0x66),
                Color.FromArgb(0xFF,0x00,0x00,0x00)
            };

        private int _currentColorIndex = 0;
        public Brush AxisBrush { get { return _AxisDefaultBrush; } }
        public Brush SubAxisBrush { get { return _SubAxisDefaultBrush; } }
        public double AxisLength { get { return _AxisDefaultLength; } }
        public Point AxisCenter { get { return _AxisDefaultCenter; } }
        public double AxisArrowSize { get { return _AxisArrowSize; } }
        public double LetterShift { get { return _LetterShift; } }
        public double LetterWidth { get { return _LetterWidth; } }
        public double LetterHeight { get { return _LetterHeight; } }
        public double VectorFactor { get { return _VectorFactor; } }


        public string CurrentPageUrl
        {
            get { return this._currentPageUrl; }
            set
            {
                this._currentPageUrl = value;
                var func = HtmlPage.Window.GetProperty(VECTORS_CHANGE_JS_FUNC) as ScriptObject;
                if (func != null)
                {
                    func.InvokeSelf(this._currentPageUrl);
                }
            }
        }

        public Brush NextColor
        {
            get
            {
                var brush = new SolidColorBrush(Colors[this._currentColorIndex]);
                this._currentColorIndex = (this._currentColorIndex == (Colors.Length - 1)) ? 0 : this._currentColorIndex + 1;
                this.ColorRectangle.Fill = new SolidColorBrush(Colors[this._currentColorIndex]);
                return brush;
            }
        }

        public void AddLine(C3DLib.Line2D line)
        {
            if (line.HeadUI != null)
            {
                this.GraphContainer.Children.Add(line.HeadUI);
            }
            this.GraphContainer.Children.Add(line.LineUI);
        }

        public void RemoveLine(C3DLib.Line2D line)
        {
            if (line.HeadUI != null)
            {
                this.GraphContainer.Children.Remove(line.HeadUI);
            }
            this.GraphContainer.Children.Remove(line.LineUI);
        }

        public void ResetColorIndex()
        {
            this._currentColorIndex = 0;
            this.ColorRectangle.Fill = new SolidColorBrush(Colors[this._currentColorIndex]);
        }
        #endregion
        

    }
}
